
#ifndef __MESH_ITEMSDEF_H__
#define __MESH_ITEMSDEF_H__

#include <CGAL/Cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/HalfedgeDS_vector.h>
#include <CGAL/HalfedgeDS_decorator.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <vector>
#include <iostream>

using std::vector;

/// A vertex base with a normal vector
template <class Refs, class Traits>
struct My_Vertex : public CGAL::HalfedgeDS_vertex_base< Refs, CGAL::Tag_true, typename Traits::Point_3> {
   
   typedef typename Traits::Vector_3 Vector_3;
   typedef typename Traits::Point_3  Point_3;
   typedef typename CGAL::HalfedgeDS_vertex_base<Refs>::Vertex_handle      Vertex_handle;
   typedef typename CGAL::HalfedgeDS_vertex_base<Refs>::Halfedge_handle    Halfedge_handle;
   
   My_Vertex(const Point_3& p) : 
      CGAL::HalfedgeDS_vertex_base< Refs, CGAL::Tag_true, Point_3> (p) {
      reset_clustering();
      reset_skinning();
         
      weights.reserve(40);
      ts.reserve(40);
   }
   
   My_Vertex() : 
      CGAL::HalfedgeDS_vertex_base< Refs, CGAL::Tag_true, Point_3> () {
      reset_clustering();
      reset_skinning();
   }
   
   /// Reset variables used for clustering
   void reset_clustering() {
      
      assigned = false;
      rigging_group = -1;
      next_in_component = -1;
      dClosestJoint = 0;
      curr_comp_inter = -1;
      curr_comp = -1;
      //~ correctionFlag = false;
      //~ dist_comp = 1e8;
      //~ d = 1e8;
      dPrimSkinRoot = 1e8;
      tclosest_bone = 0;
      d = 1e8;
      _source = -1;
      _inf = _sup = -1;
      _dist_sup = 0;
      _lastEC = -1;
      _ringId = -1;
      _classified = false;
      
      _band = -1;
      _idclosestSeed = -1;
      _last_seed_reaching_vertex = -1;
   }
   
   void reset_skinning() {
      
      d = 1e8;
      //~ assigned = false;
      //~ jointsweights.clear();
      weights.clear();
      ts.clear();
      
      weightlf[0] = 0;
      weightlf[1] = 0;
   }
   
   void inf(int i) { _inf = i; }
   int inf() { return _inf; }
   void sup(int s) { _sup = s; }
   int sup() { return _sup; }
   void dist_sup(float s) { _dist_sup = s; }
   float dist_sup() { return _dist_sup; }
   void source(int s) { _source = s; }
   int  source() { return _source; }
   bool is_classified() { return _classified; }
   void classified(bool f) { _classified = f; }
   void lastEC(int i) { _lastEC = i; }
   int lastEC() { return _lastEC; }
   void idcentroid(int c) { _idcentroid = c; }
   int idcentroid() { return _idcentroid; }
   void ringId(int c) { _ringId = c; }
   int ringId() { return _ringId; }
   
   void band(int i) { _band = i; }
   int band() { return _band; }
   void idclosestSeed(int i) { _idclosestSeed=i; }
   int idclosestSeed() { return _idclosestSeed; }
   void last_seed_reaching_vertex(int i) { _last_seed_reaching_vertex=i; }
   int last_seed_reaching_vertex() { return _last_seed_reaching_vertex; }
   
   void normal(const Vector_3 &n)  { _normal = n; }
   Vector_3 normal()   const { return _normal; }
   Vector_3 &normal()  { return _normal; }
   unsigned id() const  { return _id; }
   void id(unsigned i) { _id = i; }
   
   void previous(const Vertex_handle &v)  { _previous=v; }
   Vertex_handle previous() const { return _previous; }
   
   Vector_3 _normal; // Vertex normal
   Vertex_handle _previous; // the previous vertex
   
   /// clustering variables
   bool    assigned; // indicates whether the connected component of the vertex has already been found
   int     rigging_group; // bone associated to the vertex by the rigging process  /// join? or bone? ... jointgroup
   int     next_in_component;
   float   dClosestJoint;
   int     curr_comp_inter;
   int     curr_comp; // last connected component or group whose expansion reached the vertex
   float   dPrimSkinRoot;
   float   tclosest_bone;
   
   /// skinning variables
   float                    d;
   float                    weightlf[2];
   vector<float>            weights;  // weight of each closest bone or joint
   vector<float>            ts; // indicates the closest point to each bones
   vector<Vertex_handle>    neighbors;   // array of neighbors of this vertex
   vector<float>            cots; // cot(alpha)+cot(beta) values of incident halfedges of this vertex
   
   float          VA;          // Stores the Voronoi area for current vertex 
   float          sapb;        // Stores the summation of alpha and beta values (see the Laplacian coordinates definition)
   float          lfdenom;     // Laplacian filter denominator
   float          lfkterm;     // Laplacian filter constant term
private:
   unsigned       _id;
// variables used for skeleton generation using paths
   int            _source; /// associated source (seed)
   int            _inf; ///
   int            _sup; ///
   float          _dist_sup;
   bool           _classified;
   int            _lastEC;
   int            _idcentroid;   /// id of the associated centroid
   int            _ringId;
// variables used for skeleton generation using wavefronts
   int            _idclosestSeed;
   int            _last_seed_reaching_vertex;
   int            _band;
};

template <class Refs>
class Halfedge : public CGAL::HalfedgeDS_halfedge_base<Refs> {
   
public:
   Halfedge() :
      hedgeid(-1), alphaplusbeta(0) {
   }
   
   float getAlphaPlusBeta() const { return alphaplusbeta; }
   
   int      hedgeid; // halfedge id
   float    alphaplusbeta; // parameter for Laplacian coordinates computing
};

/// An item type using My_Vertex
struct My_items : public CGAL::Polyhedron_items_3 {
   
   template < class Refs, class Traits>
   struct Vertex_wrapper {
      typedef My_Vertex <Refs, Traits>          Vertex;
   };
   
   template <class Refs, class Traits>
   struct Halfedge_wrapper {
      typedef Halfedge<Refs>                    Halfedge;
   };
};

#endif
